/**
 * Example 5: Event Reset Mode Verification
 *
 * This example demonstrates how to verify the actual reset behavior of an event
 * at runtime, allowing modules to adapt to the event's actual behavior regardless
 * of how it was created.
 */

#include <Windows.h>
#include <iostream>
#include <string>
#include <sstream>

// Function to verify if an event has manual-reset or auto-reset behavior
bool VerifyEventResetBehavior(HANDLE hEvent, bool& isManualReset) {
    std::cout << "Verifying event reset behavior..." << std::endl;
    
    // First, ensure the event is in nonsignaled state
    ResetEvent(hEvent);
    
    // Create thread parameters and event for synchronization
    struct ThreadParams {
        HANDLE hTestEvent;
        HANDLE hReadyEvent;
        bool wasSignaled;
    };
    
    HANDLE hReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hReadyEvent == NULL) {
        std::cerr << "Failed to create ready event" << std::endl;
        return false;
    }
    
    // Create two threads that will wait on the event
    ThreadParams params1 = { hEvent, hReadyEvent, false };
    ThreadParams params2 = { hEvent, hReadyEvent, false };
    
    // Thread function to wait on the event
    auto threadFunc = [](LPVOID lpParam) -> DWORD {
        ThreadParams* params = static_cast<ThreadParams*>(lpParam);
        
        // Signal that the thread is ready
        SetEvent(params->hReadyEvent);
        
        // Wait for the event
        DWORD waitResult = WaitForSingleObject(params->hTestEvent, INFINITE);
        if (waitResult == WAIT_OBJECT_0) {
            params->wasSignaled = true;
        }
        
        return 0;
    };
    
    // Create the threads
    HANDLE hThread1 = CreateThread(NULL, 0, 
        static_cast<LPTHREAD_START_ROUTINE>(threadFunc), &params1, 0, NULL);
    HANDLE hThread2 = CreateThread(NULL, 0, 
        static_cast<LPTHREAD_START_ROUTINE>(threadFunc), &params2, 0, NULL);
    
    if (hThread1 == NULL || hThread2 == NULL) {
        std::cerr << "Failed to create test threads" << std::endl;
        if (hThread1) CloseHandle(hThread1);
        if (hThread2) CloseHandle(hThread2);
        CloseHandle(hReadyEvent);
        return false;
    }
    
    // Wait for both threads to be ready
    WaitForSingleObject(hReadyEvent, INFINITE);
    
    // Set the test event
    std::cout << "Setting the event to test reset behavior..." << std::endl;
    SetEvent(hEvent);
    
    // Give threads a chance to respond
    Sleep(500);
    
    // Wait for both threads to finish
    HANDLE threads[2] = { hThread1, hThread2 };
    WaitForMultipleObjects(2, threads, TRUE, INFINITE);
    
    // Check results
    bool thread1Signaled = params1.wasSignaled;
    bool thread2Signaled = params2.wasSignaled;
    
    std::cout << "Thread 1 was " << (thread1Signaled ? "signaled" : "not signaled") << std::endl;
    std::cout << "Thread 2 was " << (thread2Signaled ? "signaled" : "not signaled") << std::endl;
    
    // Both threads signaled means manual reset behavior
    // Only one thread signaled means auto reset behavior
    if (thread1Signaled && thread2Signaled) {
        std::cout << "Detected MANUAL-RESET behavior" << std::endl;
        isManualReset = true;
    } else if (thread1Signaled || thread2Signaled) {
        std::cout << "Detected AUTO-RESET behavior" << std::endl;
        isManualReset = false;
    } else {
        std::cout << "Failed to detect reset behavior (no threads were signaled)" << std::endl;
        CloseHandle(hThread1);
        CloseHandle(hThread2);
        CloseHandle(hReadyEvent);
        return false;
    }
    
    // Clean up
    CloseHandle(hThread1);
    CloseHandle(hThread2);
    CloseHandle(hReadyEvent);
    
    return true;
}

// EventWrapper class that adapts to the actual event behavior
class EventWrapper {
private:
    HANDLE m_hEvent;
    bool m_isManualReset;
    bool m_verifiedBehavior;
    std::wstring m_name;
    
public:
    EventWrapper(const std::wstring& name, bool expectedManualReset) 
        : m_name(name), m_verifiedBehavior(false) {
        // Try to open the event first
        m_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, name.c_str());
        
        // If event doesn't exist, create it with expected reset mode
        if (m_hEvent == NULL) {
            std::cout << "Creating new " << (expectedManualReset ? "manual" : "auto") 
                      << "-reset event: " << name.c_str() << std::endl;
            
            m_hEvent = CreateEvent(NULL, expectedManualReset, FALSE, name.c_str());
            m_isManualReset = expectedManualReset;
        }
        else {
            std::cout << "Opened existing event: " << name.c_str() << std::endl;
            
            // If we opened an existing event, verify its behavior
            if (!VerifyEventResetBehavior(m_hEvent, m_isManualReset)) {
                std::cerr << "Failed to verify event behavior" << std::endl;
                m_isManualReset = expectedManualReset; // Fallback to expected behavior
            }
            else {
                m_verifiedBehavior = true;
                
                if (m_isManualReset != expectedManualReset) {
                    std::cout << "WARNING: Event '" << name.c_str() 
                              << "' has " << (m_isManualReset ? "manual" : "auto")
                              << "-reset behavior, but expected "
                              << (expectedManualReset ? "manual" : "auto") 
                              << "-reset" << std::endl;
                }
            }
        }
    }
    
    ~EventWrapper() {
        if (m_hEvent != NULL) {
            CloseHandle(m_hEvent);
            m_hEvent = NULL;
        }
    }
    
    // Wait for the event with proper reset handling
    bool Wait(DWORD timeout = INFINITE) {
        std::cout << "Waiting on event '" << m_name.c_str() 
                  << "' with " << (m_isManualReset ? "manual" : "auto") 
                  << "-reset behavior" << std::endl;
        
        DWORD result = WaitForSingleObject(m_hEvent, timeout);
        bool signaled = (result == WAIT_OBJECT_0);
        
        // Handle auto-reset logic internally if manual reset event
        if (signaled && m_isManualReset) {
            std::cout << "Signaled. Using verified reset behavior" << std::endl;
        }
        
        return signaled;
    }
    
    // Reset the event (call for manual reset events)
    void Reset() {
        // For manual reset events, we need to explicitly reset
        // For auto-reset events, this is redundant but harmless
        if (m_isManualReset) {
            std::cout << "Explicitly resetting manual-reset event '" << m_name.c_str() << "'" << std::endl;
        }
        else {
            std::cout << "Event '" << m_name.c_str() << "' is auto-reset, but calling ResetEvent anyway for safety" << std::endl;
        }
        
        ResetEvent(m_hEvent);
    }
    
    // Set the event
    void Set() {
        std::cout << "Setting event '" << m_name.c_str() << "'" << std::endl;
        SetEvent(m_hEvent);
    }
    
    // Get behavior info
    bool IsManualReset() const { return m_isManualReset; }
    bool IsVerified() const { return m_verifiedBehavior; }
    HANDLE GetHandle() const { return m_hEvent; }
};

// Demonstration thread function
DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    struct ThreadParams {
        std::shared_ptr<EventWrapper> eventWrapper;
        int threadId;
    };
    
    ThreadParams* params = static_cast<ThreadParams*>(lpParam);
    auto eventWrapper = params->eventWrapper;
    int threadId = params->threadId;
    
    std::ostringstream threadName;
    threadName << "Thread " << threadId;
    std::string name = threadName.str();
    
    std::cout << name << ": Starting and waiting for event" << std::endl;
    bool signaled = eventWrapper->Wait();
    
    if (signaled) {
        std::cout << name << ": Event was signaled, processing notification" << std::endl;
        Sleep(500);  // Simulate processing
        
        // Always reset the event - our wrapper handles this appropriately
        eventWrapper->Reset();
        
        std::cout << name << ": Processing complete" << std::endl;
    }
    else {
        std::cout << name << ": Wait failed" << std::endl;
    }
    
    delete params;
    return 0;
}

int main() {
    std::cout << "Example 5: Event Reset Mode Verification\n" << std::endl;
    
    // Demonstration with two wrapper instances:
    // 1. First instance creates the event with manual reset
    // 2. Second instance expects auto reset but adapts to actual (manual reset) behavior
    
    std::cout << "\n--- Scenario 1: Creating Manual Reset Event ---" << std::endl;
    auto event1 = std::make_shared<EventWrapper>(L"Global\\Example5_Event", true);  // Manual reset
    
    // Spawn threads waiting on event1
    std::vector<HANDLE> threads;
    for (int i = 1; i <= 3; i++) {
        auto* params = new struct {
            std::shared_ptr<EventWrapper> eventWrapper;
            int threadId;
        };
        
        params->eventWrapper = event1;
        params->threadId = i;
        
        HANDLE hThread = CreateThread(NULL, 0, ThreadFunc, params, 0, NULL);
        if (hThread) threads.push_back(hThread);
    }
    
    // Wait for threads to start
    Sleep(1000);
    
    // Signal the event
    std::cout << "\nMain: Signaling the event" << std::endl;
    event1->Set();
    
    // Wait for all threads to finish
    WaitForMultipleObjects(static_cast<DWORD>(threads.size()), &threads[0], TRUE, INFINITE);
    for (auto hThread : threads) {
        CloseHandle(hThread);
    }
    threads.clear();
    
    // Now create a second event wrapper expecting auto-reset behavior
    std::cout << "\n--- Scenario 2: Opening Existing Event With Different Reset Mode ---" << std::endl;
    auto event2 = std::make_shared<EventWrapper>(L"Global\\Example5_Event", false);  // Expect auto-reset
    
    // Verify that it detected the actual behavior
    std::cout << "Event2 verification result: " 
              << (event2->IsVerified() ? "Verified" : "Not verified") << std::endl;
    std::cout << "Event2 detected behavior: " 
              << (event2->IsManualReset() ? "Manual reset" : "Auto reset") << std::endl;
    
    // Spawn more threads with event2
    for (int i = 4; i <= 6; i++) {
        auto* params = new struct {
            std::shared_ptr<EventWrapper> eventWrapper;
            int threadId;
        };
        
        params->eventWrapper = event2;
        params->threadId = i;
        
        HANDLE hThread = CreateThread(NULL, 0, ThreadFunc, params, 0, NULL);
        if (hThread) threads.push_back(hThread);
    }
    
    // Wait for threads to start
    Sleep(1000);
    
    // Signal the event again
    std::cout << "\nMain: Signaling the event again" << std::endl;
    event2->Set();
    
    // Wait for all threads to finish
    WaitForMultipleObjects(static_cast<DWORD>(threads.size()), &threads[0], TRUE, INFINITE);
    
    // Summary
    std::cout << "\nMain: Demo completed. Observations:" << std::endl;
    std::cout << "  1. The EventWrapper verifies actual event reset behavior at runtime" << std::endl;
    std::cout << "  2. It adapts to the actual behavior, regardless of expected mode" << std::endl;
    std::cout << "  3. All modules can safely use the same event without reset mode conflicts" << std::endl;
    std::cout << "  4. Best practice: Always call Reset(), and the wrapper decides if it's needed" << std::endl;
    
    // Clean up
    for (auto hThread : threads) {
        CloseHandle(hThread);
    }
    
    std::cout << "\nPress Enter to exit..." << std::endl;
    std::cin.get();
    
    return 0;
}
